package com.gylang.passport.service.token.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import com.gylang.cache.CacheManager;
import com.gylang.common.DateUtils;
import com.gylang.common.constant.CacheConstant;
import com.gylang.common.util.Asserts;
import com.gylang.passport.domain.AccessCheck;
import com.gylang.passport.domain.token.BaseToken;
import com.gylang.passport.service.token.TokenService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Map;

/**
 * @author gylang
 * data 2021/1/4
 */
public abstract class BaseTokenServiceImpl<T extends BaseToken> implements TokenService<T> {

    @Autowired
    protected CacheManager cacheManager;

    @Override
    public Map<String, Long> getAllRight(BaseToken baseToken) {

        return getAllRight(baseToken.getRightKey());
    }

    @Override
    public Map<String, Long> getAllRight(String rightKey) {
        return cacheManager.getMap(rightKey);
    }

    @Override
    public boolean checkAccessByRight(String rightKey, String right) {
        return cacheManager.mapContainKey(rightKey, rightKey);
    }

    @Override
    public boolean checkAccessByRight(String rightKey, String... right) {
        return cacheManager.mapContainKey(rightKey, right);
    }

    @Override
    public boolean checkAccessByRole(Map<String, Long> myRole, String needRole) {
        Asserts.notNull(needRole, "");
        if (CollUtil.isEmpty(myRole)) {
            return false;
        }
        Long expire = myRole.get(needRole);
        return DateUtils.gtCurrentTimeOrEver(expire);
    }

    @Override
    public boolean checkAccessByRole(BaseToken baseToken, String needRole) {

        if (null == baseToken) {
            return false;
        }
        return checkAccessByRole(baseToken.getRoles(), needRole);
    }

    @Override
    public boolean checkAccessByRole(Map<String, Long> myRole, String... needRole) {

        boolean containAll = true;

        if (CollUtil.isEmpty(myRole)) {
            return false;
        }
        for (String role : needRole) {
            Long expire = myRole.get(role);
            containAll = containAll && DateUtils.gtCurrentTimeOrEver(expire);
        }
        return containAll;
    }

    @Override
    public boolean checkAccess(AccessCheck accessCheck) {

        // 只需要登录
        if (accessCheck.isJustLogin()) {
            return checkLogin(accessCheck.getAuthToken());
        }
        T token = getToken(accessCheck.getAuthToken());
        // 判断是否包含角色校验
        boolean roleAccess = checkRoleAccess(accessCheck, token);
        // 判断权限
        boolean rightAccess = checkRightAccess(accessCheck, token);
        // 角色和权限关系
        if (accessCheck.isRightAndRole()) {
            return rightAccess && roleAccess;
        } else {
            return rightAccess || roleAccess;
        }
    }

    private boolean checkRoleAccess(AccessCheck accessCheck, T token) {

        boolean roleAccess = true;
        List<String> needRoles = accessCheck.getRole();
        if (CollUtil.isNotEmpty(needRoles)) {
            Map<String, Long> roles = token.getRoles();
            // 不存在角色 不通过
            if (CollUtil.isNotEmpty(roles)) {
                return false;
            }
            // 并关系
            if (accessCheck.isRoleIsAnd()) {
                roleAccess = checkAccessByRole(roles, ArrayUtil.toArray(needRoles, String.class));
            } else {
                // 或环形
                boolean hasRole = false;
                for (String needRole : needRoles) {
                    // 只要有一个为ture 那就是true
                    hasRole = hasRole || roles.containsKey(needRole);
                }
                roleAccess = hasRole;
            }
        }
        return roleAccess;
    }

    private boolean checkRightAccess(AccessCheck accessCheck, T token) {

        boolean rightAccess = true;
        List<String> needRights = accessCheck.getRight();
        if (CollUtil.isNotEmpty(needRights)) {
            Map<String, Long> rights = getAllRight(token.getRightKey());
            // 不存在权限 不通过
            if (CollUtil.isNotEmpty(rights)) {
                return false;
            }
            // 并关系
            if (accessCheck.isRightIsAnd()) {
                for (String needRole : needRights) {
                    // 如果一个为false 那就是false
                    rightAccess = rightAccess && rights.containsKey(needRole);
                }
            } else {
                boolean hasRight = false;
                for (String needRole : needRights) {
                    // 如果一个为true 那就是true
                    hasRight = hasRight || rights.containsKey(needRole);
                }
                rightAccess = hasRight;
            }
        }
        return rightAccess;
    }

    @Override
    public String assemblyAuthToken(String tokenId) {
        return CacheConstant.AUTH_TOKEN_PREFIX + tokenId;
    }
}
